package com.strato.orm.mybatis.v1_0_0.annotation.handler;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.osgi.framework.Bundle;

import com.strato.annotation.base.v1_0_0.handler.AbstractAnnotationHandler;
import com.strato.base.aop.v1_0_0.AopFactory;
import com.strato.base.bean.v1_0_0.BeanUtil;
import com.strato.base.env.v1_0_0.GlobalEnvironment;
import com.strato.logging.v1_0_0.api.Log;
import com.strato.logging.v1_0_0.api.LogFactory;
import com.strato.orm.mybatis.v1_0_0.activator.Activator;
import com.strato.orm.mybatis.v1_0_0.annotation.MybatisAlias;
import com.strato.orm.mybatis.v1_0_0.annotation.MybatisMapper;
import com.strato.orm.mybatis.v1_0_0.annotation.MybatisWired;

public class MybatisMapperAnnotationHandler extends AbstractAnnotationHandler {

	private Log log=LogFactory.getLog(getClass());
	
	private Map<String,LocationDef> locations=new HashMap<String,LocationDef>();
	private Map<String,AliasDef> aliases=new HashMap<String,AliasDef>();
	
	public Map<String, LocationDef> getLocations() {
		return locations;
	}

	@Override
	public void handleClass(Bundle bundle, Class<?> clazz, Annotation annotation) {
		boolean b1=handleMapper(bundle,clazz,annotation);
		boolean b2=handleAlias(bundle,clazz,annotation);
		if(b1||b2){
			Activator.getObserver().speedup();
		}
	}

	private boolean handleAlias(Bundle bundle, Class<?> clazz, Annotation annotation) {
		if(!MybatisAlias.class.equals(annotation.annotationType())){
			return false;
		}
		MybatisAlias aliasAnnotation = clazz.getAnnotation(MybatisAlias.class);
		String name=aliasAnnotation.value();
		String className=clazz.getName();
		if(aliases.containsKey(name)){
			log.warn("Duplicate mybatis alias "+name+",skipping "+className);
			return false;
		}
		AliasDef alias=new AliasDef();
		alias.bundle=bundle.getSymbolicName();
		alias.className=clazz.getName();
		alias.name=name;
		aliases.put(name, alias);
		log.info("Registered mybatis alias "+name);
		return true;
	}

	@Override
	public void handleFieldOnInstantiate(Bundle bundle, Field field, Class<?> clazz, Object object, Annotation fieldAnnotation) {
		if (!MybatisWired.class.equals(fieldAnnotation.annotationType())) {
			return;
		}
		{
			Object fieldValue = createMapperProxy(bundle, field, clazz, object, fieldAnnotation);
			Class<?> wrapperClass=object.getClass();
			Field wrapperField = BeanUtil.getField(wrapperClass, field.getName());
			boolean accessible = wrapperField.isAccessible();
			try {
				wrapperField.setAccessible(true);
				wrapperField.set(object, fieldValue);
				log.info("Wired "+fieldValue.getClass()+" to "+object+"."+field.getName());
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e);
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e);
			} finally {
				wrapperField.setAccessible(accessible);
			}
		}
	}

	private Object createMapperProxy(Bundle bundle, Field field, Class<?> clazz, Object object, Annotation fieldAnnotation) {
		String bundleName=bundle.getSymbolicName();
		String dataSourceName=field.getAnnotation(MybatisWired.class).datasource();
		dataSourceName=GlobalEnvironment.getInstance().resolveString(dataSourceName);
		MybatisMapperInvocationInterceptor interceptor = new MybatisMapperInvocationInterceptor(bundleName,field.getType().getName(),dataSourceName);
		Object proxy= AopFactory.createProxy(field.getType(), interceptor);
		return proxy;
	}

	private boolean handleMapper(Bundle bundle, Class<?> clazz, Annotation annotation) {
		if(!clazz.isInterface()){
			return false;
		}
		if(!MybatisMapper.class.equals(annotation.annotationType())){
			return false;
		}
		MybatisMapper mapper=clazz.getAnnotation(MybatisMapper.class);
		String configLocation=mapper.value();
		String configType=mapper.type();
		if(MybatisMapper.TYPE_XML.equals(configType)&&"".equals(configLocation)){//ȡ../xml/[CLass].xml
			String packageName = clazz.getPackage().getName();
			{
				int index = packageName.lastIndexOf(".");
				packageName=packageName.substring(0,index);
			}
			{
				StringBuffer buf=new StringBuffer();
				buf.append(packageName.replaceAll("\\.","/")).append("/xml/");
				buf.append(clazz.getSimpleName()).append(".xml");
				configLocation=buf.toString();
			}
		}else{
			configLocation=clazz.getName();
		}
		LocationDef location=new LocationDef();
		location.bundle=bundle.getSymbolicName();
		location.location=configLocation;
		location.type=configType;
		location.lastModified=bundle.getLastModified();
		locations.put(clazz.getName(),location);
		log.info("Registered mybatis mapper "+clazz);
		return true;
	}

	public class LocationDef{
		public String location;
		public String bundle;
		public String type;
		public long lastModified;
		public String getLocation() {
			return location;
		}
		public void setLocation(String location) {
			this.location = location;
		}
		public String getBundle() {
			return bundle;
		}
		public void setBundle(String bundle) {
			this.bundle = bundle;
		}
		public String getType() {
			return type;
		}
		public void setType(String type) {
			this.type = type;
		}
		public long getLastModified() {
			return lastModified;
		}
		public void setLastModified(long lastModified) {
			this.lastModified = lastModified;
		}
		
	}
	public class AliasDef{
		public String name;
		public String className;
		public String bundle;
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String getClassName() {
			return className;
		}
		public void setClassName(String className) {
			this.className = className;
		}
		public String getBundle() {
			return bundle;
		}
		public void setBundle(String bundle) {
			this.bundle = bundle;
		}
		
	}
	
	public void clear(String bundleName){
		for(Iterator<Entry<String, LocationDef>> iter = locations.entrySet().iterator();iter.hasNext();){
			Entry<String, LocationDef> entry = iter.next();
			LocationDef location = entry.getValue();
			if(bundleName.equals(location.bundle)){
				iter.remove();
			}
		}
		for(Iterator<Entry<String, AliasDef>> iter = aliases.entrySet().iterator();iter.hasNext();){
			Entry<String, AliasDef> entry = iter.next();
			AliasDef alias = entry.getValue();
			if(bundleName.equals(alias.bundle)){
				iter.remove();
			}
		}
	}

	private boolean locked=false;
	
	public boolean isLocked(){
		return locked;
	}
	
	public synchronized void lock(){
		locked=true;
	}
	
	public synchronized void unlock(){
		locked=false;
	}
	
	@Override
	public boolean isReady() {
		return super.isReady()&&!isLocked();
	}
	
	
}
